from Lib import Util
import datetime

GRADIENTS = ['peer', 'as']
GRADIENT_LABELS = ['Peer Activity', 'Intermediate Activity']

MIN_GRADIENT_WIDTH = 300 #minimum gradient size if we're doing no shrinking/expanding to resulting svg
NUM_SIZE_NODES = 5
NUM_SWITCH_LABELS = 4

FONT_FAMILY_STR = "sans-serif"
GRADIENT_DEF_STR = '<defs>\n\
    <!-- Gradient for peer rectangle -->\n\
    <linearGradient id="peer_gradient" x1="0%" y1="0%" x2="100%" y2="0%">\n\
        <stop offset="0%" style="stop-color:rgb(255,255,255); stop-opacity:1"/>\n\
        <stop offset="100%" style="stop-color:rgb(255,90,0); stop-opacity:1"/>\n\
    </linearGradient>\n\n\
    <!-- Gradient for AS rectangle -->\n\
    <linearGradient id="as_gradient" x1="0%" y1="0%" x2="100%" y2="0%">\n\
        <stop offset="0%" style="stop-color:rgb(255,255,255); stop-opacity:1"/>\n\
        <stop offset="100%" style="stop-color:rgb(0,51,204); stop-opacity:1"/>\n\
    </linearGradient>\n\
</defs>\n'

class KeyGen:
    def __init__(self, maxWidth):
        self.keyHeight = -1
        self.graphHeight = -1
        self.graphWidth = -1
        self.scale = 1
        self.maxWidth = maxWidth
        self.curHeight = 0
        self.keyStr = None
        self.mouseOverStr = None
        self.sizes = {}
        self.prefix = None
        self.startDate = None
        self.endDate = None
        self.sizes['nodeHeightMax'] = .75 * 72
        self.sizes['nodeHeightMin'] = .4 * 72
        self.sizes['nodeWidthMax'] = self.sizes['nodeHeightMax'] * 1.75
        self.sizes['nodeWidthMin'] = self.sizes['nodeHeightMin'] * 1.75
          
    def setSize(self, width = None, height = None, scale = None):
        if width:
            self.graphWidth = width
        if height:
            self.graphHeight = height
        if scale:
            self.scale = scale
            
        self.graphWidth /= self.scale
        self.graphHeight /= self.scale
        
        coeff = max(1, float(self.graphWidth)/self.maxWidth)
        self.sizes['keyNodePad'] = 10 * coeff
        self.sizes['keyPad'] = 40 * coeff
        self.sizes['keyLabel'] = 20 * coeff
        self.sizes['keyGradLabel'] =  18 * coeff
        self.sizes['keyPercent'] = 18 * coeff
        self.sizes['keyBorder'] = 3 * coeff
        self.sizes['moLabel'] = 13 * coeff
        self.sizes['moPrefixLabel'] = 16 * coeff
        self.sizes['moGradHeight'] = 20 * coeff
        self.sizes['moPad'] = 7 * coeff
        self.sizes['moTriangle'] = 5 * coeff
        self.sizes['moStroke'] = 2 * coeff
        self.sizes['moLine'] = 4 * coeff
        self.sizes['titlePrefix'] = 30 * coeff
        self.sizes['titleDate'] = 20 * coeff
        self.sizes['titlePad'] = 15 * coeff
        
        self.sizes['percentNodesWidth'] = (self.sizes['nodeWidthMin'] + self.sizes['nodeWidthMax']) *\
         float(NUM_SIZE_NODES)/2 + self.sizes['keyNodePad'] * 4  
      
        minGraphWidth = max(self.sizes['percentNodesWidth'] + self.sizes['keyPad'] * 2, MIN_GRADIENT_WIDTH + self.sizes['keyPad'] * 2) +\
        self.sizes['keyBorder'] * 2
        if (self.graphWidth < minGraphWidth):
            self.graphWidth = minGraphWidth
            
        self.sizes['keyNodePad'] += (self.graphWidth - self.sizes['keyPad'] * 2 - self.sizes['percentNodesWidth'])/4            
           
    def createGraphKey(self): 
        self.keyStr = GRADIENT_DEF_STR
        self.keyStr += '\n<g id="key" class="graph" transform="scale(%s %s) translate(0 0)" font-family="%s">\n' %\
         (self.scale, self.scale, FONT_FAMILY_STR)
        
        self.curHeight = 0
        
        self.keyStr += self.createTitleStr()
        keyStartHeight = self.curHeight
        
        self.keyStr += self.createKeyTitleAndControls() 
        self.keyStr += self.createPercentActiveBoxes()          
        self.keyStr += self.createGradients()
        
        self.keyStr += '<rect x="%s" y="%s" width="%s" height="%s" fill="none" stroke="black" stroke-width="3"/>\n' %\
            (self.sizes['keyBorder']/2, keyStartHeight, self.graphWidth - self.sizes['keyBorder'], self.curHeight)
        self.keyStr += '\n</g>\n'

        return
    
    def createTitleStr(self):
        self.curHeight += self.sizes['titlePad'] + self.sizes['titlePrefix']
        titleStr = '\t<g class="graph" transform="translate(0 0)">\n'
        titleStr += '\t\t<text text-anchor="middle" x="%s" y="%s" font-size="%s">Map of %s</text>\n' %\
         (self.graphWidth/2, self.curHeight, self.sizes['titlePrefix'], self.prefix)
        self.curHeight += self.sizes['titleDate']
        sd = self.startDate.strftime("%B %d, %Y") 
        ed = self.endDate.strftime("%B %d, %Y") 
        titleStr += '\t\t<text text-anchor="middle" x="%s" y="%s" font-size="%s">%s to %s</text>\n' %\
         (self.graphWidth/2, self.curHeight, self.sizes['titleDate'], sd, ed)
        titleStr += '\t</g>\n\n'
        self.curHeight += self.sizes['titlePad']
        return titleStr
    
    def createKeyTitleAndControls(self):
        self.curHeight += self.sizes['keyLabel'] * 1.2
        controlStr = '<text text-anchor="middle" x="%s" y="%s" font-size="%s">Key/Controls</text>\n' %\
        (self.graphWidth/2, self.curHeight, self.sizes['keyLabel']*1.2)
        self.curHeight += self.sizes['keyPad'] / 2 + self.sizes['keyLabel']
        
        controlStr += '''<text text-anchor="start" x="%s" y="%s" font-size="%s" font-weight="bold">Show: <tspan id="controlsShowAll" text-decoration="underline" fill="#0000FF" onclick="toggleNodes('all')" >All</tspan> | <tspan id="controlsShowEndActive" onclick="toggleNodes('end')">Ended Active</tspan> | <tspan id="controlsShowPercent" onclick="toggleNodes('percent')"> Active > 5%</tspan></text>''' %\
        (self.sizes['keyPad'], self.curHeight, self.sizes['keyLabel'])
        
    def createPercentActiveBoxes(self):
        percentActiveStr = '\t<!--Percent Active Label-->\n'
        self.curHeight += self.sizes['keyLabel']
        percentActiveStr += '\t<g id="percent_active_key" class="graph" transform="translate(0 0)">\n'
        percentActiveStr += '\t\t<text text-anchor="middle" x="%s" y="%s" font-size="%s">Time Active</text>\n'\
            % (self.graphWidth/2, self.curHeight - self.sizes['keyLabel'] * .35, self.sizes['keyLabel'])
        percentActiveStr += '\t\t<!-- 0% Active Rectangle -->\n'
        totalWidth = 0
        for i in range(0, NUM_SIZE_NODES):
            percent = ((100.0/(NUM_SIZE_NODES-1)) * i)/100.0
            percentAsStr = '%2.0f%%' % (percent * 100.0)
            boxHeight = (percent * .35 + .4) * 72
            boxWidth = 1.75 * boxHeight
            x = totalWidth
            y = (1-percent)*14
            percentActiveStr += '\t\t<rect x="%s" y="%s" width="%s" height="%s" fill="none" stroke="black" stroke-width="%s"/>\n'\
                % (self.sizes['keyPad'] + x, self.curHeight + y, boxWidth, boxHeight, Util.POLY_STROKE_WIDTH)
            percentActiveStr += '\t\t<text text-anchor="middle" x="%s" y="%s" font-size="%s" >%s</text>\n'\
                % (self.sizes['keyPad'] + x + boxWidth/2, self.curHeight + y + boxHeight/2 + self.sizes['keyPercent']/3, \
                   self.sizes['keyPercent'], percentAsStr)
            totalWidth += (boxWidth + self.sizes['keyNodePad'])
            
        self.curHeight += self.sizes['nodeHeightMax'] + self.sizes['keyPad']
        percentActiveStr += '\t</g>\n'
        return percentActiveStr
    
    def createGradients(self):
        if self.graphWidth < MIN_GRADIENT_WIDTH * 2 + self.sizes['keyPad'] * 4:
            gradientWidth = self.graphWidth - self.sizes['keyPad'] * 2            
            gradientXShifts = (self.sizes['keyPad'], self.sizes['keyPad'])
            gradientYShifts = (0, self.sizes['nodeHeightMax'] + self.sizes['keyGradLabel'] + self.sizes['keyPad'])
        else:
            gradientWidth = (self.graphWidth - self.sizes['keyPad'] * 4)/2
            gradientXShifts = (self.sizes['keyPad'], self.sizes['keyPad'] * 3 + gradientWidth)
            gradientYShifts = (0, 0)
            
        gradientStr = ''       
        for i in range(0, len(GRADIENTS)):
            gradientStr += '\t<g id="%s_gradient_key" class="graph" transform="translate(%s %s)">\n'\
                % (GRADIENTS[i], gradientXShifts[i], self.curHeight + gradientYShifts[i])
            gradientStr += '\t\t<!--Path Switches (%s) Label-->\n' % (GRADIENTS[i])
            gradientStr += '\t\t<text text-anchor="middle" x="%s" y="%s" font-size="%s">%s</text>\n\n'\
                % (gradientWidth / 2, -0.5 * self.sizes['keyLabel'], self.sizes['keyLabel'], GRADIENT_LABELS[i])
            gradientStr += '\t\t<!-- Path Switches (%s) Rectangle -->\n' % (GRADIENTS[i])
            gradientStr += '\t\t<rect x="%s" y="%s" width="%s" height="%s" fill="url(#%s_gradient)" stroke="black" stroke-width="%s" />\n'\
                % (0, 0, gradientWidth, self.sizes['nodeHeightMax'], GRADIENTS[i], Util.POLY_STROKE_WIDTH)
            labelScale = float(gradientWidth)/(NUM_SWITCH_LABELS - 1)
            for j in range(0, NUM_SWITCH_LABELS):
                label = '%.0f' % (10 ** ((5.0 * j)/(NUM_SWITCH_LABELS-1)) - 1)
                if (j == NUM_SWITCH_LABELS - 1):
                    label += '+'
                gradientStr += '\t\t<text text-anchor="middle" x="%s" y="%s" font-size="%s">%s</text>\n'\
                    % (labelScale * j, self.sizes['nodeHeightMax'] + self.sizes['keyGradLabel'], self.sizes['keyGradLabel'], label)
            gradientStr += '\t</g>\n\n'
        
        self.curHeight += gradientYShifts[1]
        self.keyHeight = self.curHeight + self.sizes['keyPad']*2 + self.sizes['keyGradLabel'] + self.sizes['keyLabel'] +\
        self.sizes['keyBorder'] * 2
        return gradientStr
    
    def createMouseOverBox(self, elapsedTime, prefix, destAsMap, prefixStats):
        self.mouseOverStr = '\n<g class="graph" transform="scale(%s %s)">\n' % (self.scale, self.scale)  
        self.mouseOverStr += self.getMouseOverNodeStr(elapsedTime)
        self.mouseOverStr += self.getMouseOverEdgeStr(elapsedTime)
        self.mouseOverStr += self.getMouseOverPrefixStr(prefix, destAsMap, prefixStats)
        self.mouseOverStr += '</g>\n'

    def getMouseOverNodeStr(self, elapsedTime):
        #node mouse over box
        self.curHeight = 0        
        mouseOverNode = '\n<g id="nodeDisplay" class="graph" transform="translate(0 0)" visibility="hidden" font-family="%s">\n' %\
        FONT_FAMILY_STR
        mouseOverNode += '\t<rect id="nodeRect" x="0" y="0" width="1" height="1" fill="#FFFFFF" stroke="black" stroke-width="%s"/>\n' %\
            (self.sizes['moStroke'])
            
        #AS Label and horizontal line break
        self.curHeight += self.sizes['moLabel'] + self.sizes['moPad']    
        mouseOverNode += '\t<text id = "nodePrimaryText" text-anchor="start" x="%s" y="%s" font-size="%s" >\n\
            \t<tspan id="nodeAsNum" font-weight="bold">AS-1</tspan>\n\
            \t<tspan id="nodeAsOwner" font-weight="normal">(Unknown)</tspan>\n\
        </text>\n' % (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'])
        self.curHeight += self.sizes['moLabel'] + self.sizes['moPad']
        mouseOverNode += '\t<text id="nodeSecondaryText" text-anchor="start" x="%s" y="%s" font-size="%s" >Default</text>\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'])
        self.curHeight += self.sizes['moPad']
        mouseOverNode += '\t<line id="nodeDisplayLine" x1="%s" y1="%s" x2="0" y2="%s" stroke-width="%s" stroke="black"/>\n' %\
            (self.sizes['moPad'], self.curHeight, self.curHeight, self.sizes['moStroke'])
        
        #switch gradient code
        self.curHeight += self.sizes['moPad'] + self.sizes['moLabel']        
        mouseOverNode += '\t<text text-anchor="start" x="%s" y="%s"  font-size="%s" >Switch Count</text>\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'])
        self.curHeight += self.sizes['moPad']        
        mouseOverNode += '\t<rect id="nodeGradientRect" x="%s" y="%s" width="0" height="%s" fill="url(null)" stroke="black" \
        stroke-width="%s" />\n' %(self.sizes['moPad'], self.curHeight, self.sizes['moGradHeight'], self.sizes['moStroke'])
        self.curHeight += self.sizes['moGradHeight']             
        mouseOverNode += '\t<g id="nodeSwitchLabel" class="graph" transform="translate(0 %s)">\n' % self.curHeight
        mouseOverNode += '\t\t<polygon points="0,-%s -%s,%s %s,%s" fill="#000000"/>\n' %\
            (self.sizes['moTriangle'], self.sizes['moTriangle'], self.sizes['moTriangle'], self.sizes['moTriangle'], self.sizes['moTriangle'])
        mouseOverNode += '\t\t<text id="nodeSwitchCount" text-anchor="middle" x="0" y="%s" font-size="%s">0</text>\n' %\
            (self.sizes['moTriangle'] + self.sizes['moLabel'], self.sizes['moLabel'])
        mouseOverNode += '\t</g>\n'
        
        #percent active code
        self.curHeight += self.sizes['moTriangle'] * 2 + self.sizes['moLabel'] * 2 + self.sizes['moPad']
        mouseOverNode += '\t<text text-anchor="start" x="%s" y="%s"  font-size="%s" >Time Active</text>\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'])
        self.curHeight += self.sizes['moPad']
        mouseOverNode += '\t<text text-anchor="start" x="%s" y="%s"  font-size="%s" >0</text>\n' %\
            (self.sizes['moPad'], self.curHeight + self.sizes['moLabel'], self.sizes['moLabel'])
        mouseOverNode += '\t<rect id="nodePercentRect" x="%s" y="%s" width="0" height="%s" fill="none" stroke="black" stroke-width="%s" />\n' %\
            (self.sizes['moPad'] + self.sizes['moLabel'], self.curHeight, self.sizes['moGradHeight'], self.sizes['moStroke'])
        mouseOverNode += '\t<text id="nodeTimeElapsedText" text-anchor="end" x="0" y="%s" font-size="%s">%s days</text>\n' %\
            (self.curHeight + self.sizes['moLabel'], self.sizes['moLabel'], elapsedTime.days)
        self.curHeight += self.sizes['moGradHeight']
        mouseOverNode += '\t<g id="nodePercentGraph" class="graph" transform="translate(0 %s)">\n' % self.curHeight
        mouseOverNode += '\t\t<line x1="0" y1="0" x2="0" y2="%s" stroke-width="%s" stroke="black"/>\n' %\
            (-self.sizes['moGradHeight'], self.sizes['moLine'])
        mouseOverNode += '\t\t<text id="nodePercentActiveText" text-anchor="middle" x="0" y="%s" font-size="%s">-1</text>\n' %\
            (self.sizes['moLabel'], self.sizes['moLabel'])
        mouseOverNode += '\t</g>\n'
        
        mouseOverNode += '</g>\n'
        return mouseOverNode
        
    def getMouseOverEdgeStr(self, elapsedTime):
        self.curHeight = self.sizes['moLabel'] + self.sizes['moPad']
        mouseOverEdge = '\n<g id="edgeDisplay" class="graph" transform="translate(0 0)" visibility="hidden" font-family="%s">\n' %\
        FONT_FAMILY_STR
        mouseOverEdge += '\t<rect id="edgeRect" x="0" y="0" width="1" height="1" fill="#FFFFFF" stroke="black" stroke-width="%s"/>\n' %\
            (self.sizes['moStroke'])
            
        #AS labels and horizontal line        
        mouseOverEdge += '\t<text id = "edgeFromAsText" text-anchor="start" x="%s" y="%s" font-size="%s" >\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'])
        mouseOverEdge += '\t\t<tspan id="edgeFromAsNum" font-weight="bold">AS-1</tspan>\n'
        mouseOverEdge += '\t\t<tspan id="edgeFromAsOwner">(Unknown)</tspan>\n'
        mouseOverEdge += '\t</text>\n'
        self.curHeight += self.sizes['moLabel'] + self.sizes['moPad']
        mouseOverEdge += '\t<text text-anchor="start" x="%s" y="%s" font-size="%s">to</text>\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'])
        self.curHeight += self.sizes['moLabel'] + self.sizes['moPad']
        mouseOverEdge += '\t<text id = "edgeToAsText" text-anchor="start" x="%s" y="%s" font-size="%s" >\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'])
        mouseOverEdge += '\t\t<tspan id="edgeToAsNum" font-weight="bold">AS-1</tspan>\n'
        mouseOverEdge += '\t\t<tspan id="edgeToAsOwner">(Unknown)</tspan>\n'
        mouseOverEdge += '\t</text>\n'
        self.curHeight += self.sizes['moPad']
        mouseOverEdge += '\t<line id="edgeDisplayLine" x1="%s" y1="%s" x2="0" y2="%s" stroke-width="%s" stroke="black"/>\n' %\
            (self.sizes['moPad'], self.curHeight, self.curHeight, self.sizes['moStroke'])
       
        #percent active code
        self.curHeight += self.sizes['moPad'] + self.sizes['moLabel']
        mouseOverEdge += '\t<text text-anchor="start" x="%s" y="%s"  font-size="%s" >Time Active</text>\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'])
        self.curHeight += self.sizes['moPad']
        mouseOverEdge += '\t<text text-anchor="start" x="%s" y="%s"  font-size="%s" >0</text>\n' %\
            (self.sizes['moPad'], self.curHeight + self.sizes['moLabel'], self.sizes['moLabel'])
        mouseOverEdge += '\t<rect id="edgePercentRect" x="%s" y="%s" width="0" height="%s" fill="none" stroke="black" stroke-width="%s" />\n' %\
            (self.sizes['moPad'] + self.sizes['moLabel'], self.curHeight, self.sizes['moGradHeight'], self.sizes['moStroke'])
        mouseOverEdge += '\t<text id="edgeTimeElapsedText" text-anchor="end" x="0" y="%s" font-size="%s">%s days</text>\n' %\
            (self.curHeight + self.sizes['moLabel'], self.sizes['moLabel'], elapsedTime.days)
        self.curHeight += self.sizes['moGradHeight']
        mouseOverEdge += '\t<g id="edgePercentGraph" class="graph" transform="translate(0 %s)">\n' % self.curHeight
        mouseOverEdge += '\t\t<line x1="0" y1="0" x2="0" y2="%s" stroke-width="%s" stroke="black"/>\n' %\
            (-self.sizes['moGradHeight'], self.sizes['moLine'])
        mouseOverEdge += '\t\t<text id="edgePercentActiveText" text-anchor="middle" x="0" y="%s" font-size="%s">-1</text>\n' %\
            (self.sizes['moLabel'], self.sizes['moLabel'])
        mouseOverEdge += '\t</g>\n'                
                    
        mouseOverEdge += '</g>\n'
        return mouseOverEdge
    
    def getMouseOverPrefixStr(self, prefix, destAsMap, prefixStats):
        self.curHeight = self.sizes['moPrefixLabel'] + self.sizes['moPad']
        width = self.graphWidth/5
        mouseOverPrefix = '\n<g id="prefixDisplay" class="graph" transform="translate(0 0)" visibility="hidden" font-family="%s">\n' %\
        FONT_FAMILY_STR
        mouseOverPrefix += '\t<rect id="prefixRect" x="0" y="0" width="50" height="50" fill="#FFFFFF" stroke="#FFD400" stroke-width="%s"/>\n' %\
            (self.sizes['moStroke'])

        #AS labels and horizontal line        
        mouseOverPrefix += '\t<text text-anchor="start" x="%s" y="%s" font-size="%s" >\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moPrefixLabel'])
        mouseOverPrefix += '\t\t<tspan font-weight="bold">%s</tspan>\n' % prefix
        if len(destAsMap) > 1:
            print "Multihomed"
            mouseOverPrefix += '\t<tspan font-weight="normal">(Multihomed)</tspan>\n'
        mouseOverPrefix += '\t</text>\n'
        for asNum, asTitles in destAsMap.items():
            self.curHeight += self.sizes['moLabel'] + self.sizes['moPad']
            mouseOverPrefix += '\t<text text-anchor="start" x="%s" y="%s" font-size="%s" >\n\
            \t<tspan font-weight="bold">AS%s</tspan>\n\
            \t<tspan font-weight="normal">(%s)</tspan>\n\
            </text>\n' % (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'], asNum, asTitles[0])
            self.curHeight += self.sizes['moLabel'] + self.sizes['moPad']
            mouseOverPrefix += '\t<text text-anchor="start" x="%s" y="%s" font-size="%s" >%s</text>\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'], asTitles[1])
            
        self.curHeight += self.sizes['moPad']
        mouseOverPrefix += '\t<line x1="%s" y1="%s" x2="%s" y2="%s" stroke-width="%s" stroke="black"/>\n' %\
            (self.sizes['moPad'], self.curHeight, width - self.sizes['moPad'], self.curHeight, self.sizes['moStroke'])
        
        statLabels = ("BGP Updates", "Path Announcements", "Duplicate Path Announcements", "Path Withdrawals")
        for i in range(0, len(prefixStats)):
            self.curHeight += self.sizes['moPad'] + self.sizes['moLabel']
            mouseOverPrefix += '\t<text text-anchor="start" x="%s" y="%s" font-size="%s" >%s %s</text>\n' %\
            (self.sizes['moPad'], self.curHeight, self.sizes['moLabel'], prefixStats[i], statLabels[i])
        mouseOverPrefix += '</g>\n'
        return mouseOverPrefix